package com.sefon.autocreate;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.xssf.usermodel.XSSFCell;
import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import com.sefon.autocreate.Column.TYPE;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;


/**
 * 
* Copyright: Copyright (c) 2018 SefonSoft
* 
* @ClassName: AnalysisExcel2File.java
* @Description: 自动生成代码
*
* @version: v1.0.0
* @author: SF2170
* @date: 2018年3月22日上午12:09:39
*
 */
public class AnalysisExcel2File {

	/**
	 * 常量
	 */
	private static String path = "";
	private static final String SPARATER = "\\" ;
	private static final String UNDERLINE = "_" ;
	private static final String LONGSTRING = "\\|" ;
	
	/**
	 * 文件输出设置  当有同名文件存在时采取的操作
	 * 0:换名生成        1:覆盖 (默认)        2: 不覆盖
	 */
	private static int kg = 1; 

	/**
	 * 需要读取的文件夹位置
	 */
	private static final String SOURCE_DIR_PATH = "C:\\Users\\asus\\Desktop\\智慧经济项目\\smart";
	
	/**
	 * 生成目标文件夹
	 */
	private static final String TARGER_DIR_PATH = "\\src\\main\\java\\com\\sefon\\smarteconomy\\";
	
	/**
	 * domain路径
	 */
	private static final String DOMAIN_PATH = "domain";
	
	/**
	 * dao路径
	 */
	private static final String DAO_PATH = "dao";
	
	/**
	 * service路径
	 */
	private static final String SERVICE_PATH = "service";
	/**
	 * service impl路径
	 */
	private static final String SERVICE_IMPL_PATH = "impl";

	static {
		URL u = Thread.currentThread().getContextClassLoader().getResource("");
		File f = new File(u.toString());
		path = f.getParentFile().getParent().substring(6);
	}

	
	public static void main(String[] args) throws IOException, InterruptedException, ClassNotFoundException {
		File file = new File(SOURCE_DIR_PATH);
		String path = file.getPath();
		String[] list = file.list();
		try {
			for (String s : list) {
				String prefix = s.substring(s.lastIndexOf(".") + 1); // 获取后缀名
				if ("xlsx".equals(prefix)) {
					AnalysisExcel2File analysisExcel2File = new AnalysisExcel2File();
					//读取Excel, 准备数据
					List<BasicTableInfo> tableList = analysisExcel2File.readExcel(path + SPARATER + s);
					// 创建实体类
					for (BasicTableInfo basicTableInfo : tableList) {
						//生成实体Bean
						analysisExcel2File.createJavaBean(basicTableInfo);
						// 生成dao
						analysisExcel2File.creatDaoFile(basicTableInfo);
						// 生成service
						analysisExcel2File.creatServiceFile(basicTableInfo);
						// 生成service实现
						analysisExcel2File.creatServiceImplFile(basicTableInfo);

					}
				}

			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		System.out.println("运行成功！");

	}


	/**
	 * 
	* @Description: 读取excel值
	*
	* @param path 文件路径
	* @return
	* @throws IOException
	* @return：
	* @throws：
	*
	* @version: v1.0.0
	* @author: SF2170
	 * @throws ClassNotFoundException 
	* @date: 2018年3月22日 上午12:28:11 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月22日     SF2170           v1.0.0               修改原因
	 */
	public List<BasicTableInfo> readExcel(String path) throws IOException, ClassNotFoundException {
		File file = new File(path);
		FileInputStream is = new FileInputStream(file);
		String fileName = file.getName();//文件名
		return analysisDataToObject(is, fileName);
	}

	/**
	 * 
	* @Description: 解析文件数据成对象
	*
	* @param is 数据流
	* @param fileName 文件名称
	* @return
	* @throws IOException
	* @return：
	* @throws：
	*
	* @version: v1.0.0
	* @author: SF2170
	 * @throws ClassNotFoundException 
	* @date: 2018年3月22日 上午12:33:51 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月22日     SF2170           v1.0.0               修改原因
	 */
	public List<BasicTableInfo> analysisDataToObject(InputStream is, String fileName) throws IOException, ClassNotFoundException {
		List<BasicTableInfo> list = new ArrayList<>();
		@SuppressWarnings("resource")
		XSSFWorkbook wb = new XSSFWorkbook(is);
		XSSFSheet sheet = wb.getSheetAt(0);//扫描第一页

		int num = sheet.getLastRowNum();
		//缓存对象
		BasicTableInfo cacheObject = null;
		String tableName = "";
		// 获取表数据
		for (int i = 2; i <= num; i++) {
			XSSFRow row = sheet.getRow(i);
			//默认同文件中表名不能一样
			XSSFCell c2 = row.getCell(1);
			if (c2 != null && c2.getStringCellValue() != null) {
				String t = removeChangeLineStr(c2.getStringCellValue());
				tableName = replaceSpecStr(t);
				//对象为空 第一次进入该方法
				if (StringUtils.isNotBlank(tableName) && (cacheObject == null ||
						!cacheObject.getTableName().equals(tableName))) {
					if (cacheObject != null) {
						list.add(cacheObject.deepClone());
					}
					cacheObject = new BasicTableInfo();
					cacheObject.setTableName(tableName);
					cacheObject.setClassName(StringUtils.capitalize(changeUnderlineStr(t)));
					XSSFCell c1 = row.getCell(0);
					cacheObject.setTableDesc(c1.getStringCellValue());
				}
			}
			Column col = new Column();
			cacheObject.getColumns().add(col);
			XSSFCell c3 = row.getCell(2);
			if (c3 != null && c3.getStringCellValue() != null ) {
				String colVal = replaceSpecStr(removeChangeLineStr(c3.getStringCellValue()));
				col.setName("`"+colVal+"`");
				col.setFieldName(StringUtils.uncapitalize(changeUnderlineStr(colVal)));
			}
			XSSFCell c4 = row.getCell(3);
			if (c4 != null && c4.getStringCellValue() != null ) {
				String colVal = replaceSpecStr(removeChangeLineStr(c4.getStringCellValue()));
				col.setType(TYPE.getTypeByKey(colVal));
				//坑 如果字符串格式必须写默认长度
				if (col.getType() == TYPE.VARCHAR) {
					col.setLength(255+"");
				}
			}
			XSSFCell c5 = row.getCell(4);
			if (c5 != null && c5.getStringCellValue() != null) {
				String[] c5Arrs = c5.getStringCellValue().split(LONGSTRING);
				for (String string : c5Arrs) {
					switch (string) {
					case "id":
						col.setIsId(true);
						cacheObject.setIdCol(col);
						break;
					case "nonull":
						col.setIsNull(false);
						break;
					case "unique":
						col.setUnique(true);
						break;
					default:
						break;
					}
				}
			}
			XSSFCell c6 = row.getCell(5);
			if (c6 != null  ) {
				c6.setCellType(CellType.STRING);
				String colVal = c6.getStringCellValue();
				if (StringUtils.isNumeric(colVal)) {
					col.setLength(colVal);
				}
			}
			XSSFCell c7 = row.getCell(6);
			if (c7 != null  ) {
				String colVal = c7.getStringCellValue();;
				col.setDesc(colVal);
			}
			XSSFCell c8 = row.getCell(7);
			if (c8 != null  ) {
				String colVal = c8.getStringCellValue();;
				col.setAnnotation(colVal);
			}
		}
		list.add(cacheObject);
		return list;
	}




	/**
	 * 创建实体类及service代码
	 * @param val	表信息
	 * @param key	表中文名
	 */
	private void createJavaBean(BasicTableInfo basicTableInfo) {
		List<String> list = new ArrayList<>();
		List<String> listEnd = new ArrayList<>();
		//设置文件头部
		setClassTopMethod(basicTableInfo, list);
		Boolean exitsDate = false;
		for (Column col : basicTableInfo.getColumns()) {
			if (StringUtils.isNotBlank(col.getAnnotation())) {
				list.add("	/**");
				list.add("	* "+ col.getAnnotation());
				list.add("	*/");
			}
			//设置Column 基本属性
			StringBuffer sb = setColumnProperty(list, col);
			String filedType = "" ;
			switch (col.getType()) {
				case DOUBLE:
					filedType = col.getType().getName();
					sb.append(", precision=12, scale=4");
					break;
				case LONG:
				case BIGINT:
					filedType = col.getType().getName();
					break;
				case DATE:
				case DATETIME:
				case TIMESTAMP:
					exitsDate = true;
					filedType = col.getType().getName();
					break;
				case TEXT:
				case LONGTEXT:
					list.add(5,"import javax.persistence.Lob;");
					list.add("	@Lob  ");
					filedType = col.getType().getName();
					break;
				default:
					filedType = col.getType().getName();
					break;
			}
			if (col.getLength() != null ) {
				filedType += "("+col.getLength()+")";
				sb.append(", length="+col.getLength()+"");
			}
			if (col.getDesc() != null ) {
				
				sb.append(", columnDefinition=\"" + filedType + " COMMENT '"+col.getDesc() +"'\"");
			} else {
				sb.append(", columnDefinition=\"" + filedType + "\"");
			}
			
			sb.append(")");
			list.add(sb.toString());
			//设置private Type filedName
			list.add("	private " + col.getType().getJavaName() +" " + col.getFieldName() + ";");
			
			list.add("");
			//设置set get方法
			setListEndMethod(listEnd, col);
		}
		if (exitsDate) {
			list.add(2,"import java.util.Date;");
		}
		list.add("");
		list.addAll(listEnd);
		list.add("}");

		// 生成文件
		creatFile(list, basicTableInfo.getClassName());
		System.out.println(basicTableInfo.getClassName()+":bean文件生成成功！");
	}


	/**
	 * 
	* @Description: 创建service文件
	*
	*
	* @version: v1.0.0
	* @author: SF2170
	 * @throws TemplateException 
	 * @throws IOException 
	* @date: 2018年3月26日 上午9:27:06 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月26日     SF2170           v1.0.0               修改原因
	 */
	private void creatServiceFile(BasicTableInfo basicTableInfo) throws IOException, TemplateException {
		String fileName =  "I" + basicTableInfo.getClassName() + "Service.java";
		Map<String, Object> map = new HashMap<>();
		map.put("objectName", basicTableInfo.getClassName());
		map.put("simName", StringUtils.uncapitalize(basicTableInfo.getClassName()));
		map.put("objectDesc", basicTableInfo.getTableDesc());
		map.put("tableName", basicTableInfo.getTableName());
		map.put("idType", basicTableInfo.getIdCol().getType().getJavaName());
		map.put("idName", basicTableInfo.getIdCol().getName().replace("`", ""));
		map.put("idClassName", basicTableInfo.getIdCol().getFieldName());
		map.put("IdName", StringUtils.capitalize(basicTableInfo.getIdCol().getFieldName()));
		map.put("timeStr", getNowTime());
		map.put("dateStr", getNowDate());
		write("TemplateService.ftl", map, TARGER_DIR_PATH  + SERVICE_PATH + SPARATER , fileName);
		System.out.println(basicTableInfo.getClassName()+":service文件生成成功！");
	}
	
	/**
	 * 
	* @Description: 创建serviceImpl文件
	*
	*
	* @version: v1.0.0
	* @author: SF2170
	 * @throws TemplateException 
	 * @throws IOException 
	* @date: 2018年3月26日 上午9:27:06 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月26日     SF2170           v1.0.0               修改原因
	 */
	private void creatServiceImplFile(BasicTableInfo basicTableInfo) throws IOException, TemplateException {
		String fileName =  basicTableInfo.getClassName() + "ServiceImpl.java";
		Map<String, Object> map = new HashMap<>();
		map.put("objectName", basicTableInfo.getClassName());
		map.put("simName", StringUtils.uncapitalize(basicTableInfo.getClassName()));
		map.put("objectDesc", basicTableInfo.getTableDesc());
		map.put("tableName", basicTableInfo.getTableName());
		map.put("idType", basicTableInfo.getIdCol().getType().getJavaName());
		map.put("idName", basicTableInfo.getIdCol().getName().replace("`", ""));
		map.put("idClassName", basicTableInfo.getIdCol().getFieldName());
		map.put("IdName", StringUtils.capitalize(basicTableInfo.getIdCol().getFieldName()));
		map.put("timeStr", getNowTime());
		map.put("dateStr", getNowDate());
		write("TemplateServiceImpl.ftl", map, TARGER_DIR_PATH  + SERVICE_PATH 
				+ SPARATER + SERVICE_IMPL_PATH + SPARATER, fileName);
		System.out.println(basicTableInfo.getClassName()+":serviceImpl文件生成成功！");
	}

	/**
	 * 
	* @Description: 创建dao文件
	*
	*
	* @version: v1.0.0
	* @author: SF2170
	* @throws TemplateException 
	* @throws IOException 
	* @date: 2018年3月26日 上午9:27:18 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月26日     SF2170           v1.0.0               修改原因
	 */
	private void creatDaoFile(BasicTableInfo basicTableInfo) throws IOException, TemplateException {
		
		String fileName =  "I" + basicTableInfo.getClassName() + "Dao.java";
		Map<String, Object> map = new HashMap<>();
		map.put("objectName", basicTableInfo.getClassName());
		map.put("objectDesc", basicTableInfo.getTableDesc());
		map.put("tableName", basicTableInfo.getTableName());
		map.put("idType", basicTableInfo.getIdCol().getType().getJavaName());
		map.put("idName", basicTableInfo.getIdCol().getName().replace("`", ""));
		map.put("idClassName", basicTableInfo.getIdCol().getFieldName());
		map.put("timeStr", getNowTime());
		map.put("dateStr", getNowDate());
		write("TemplateDao.ftl", map, TARGER_DIR_PATH  + DAO_PATH + SPARATER , fileName);
		System.out.println(basicTableInfo.getClassName()+":dao文件生成成功！");
	}


	/**
	 * 
	* @Description: 写入文件
	*
	* @param templateName ftl文件名称
	* @param map 替换映射
	* @param targetPath 目标路径
	* @param fileName 目标文件名称
	* 
	* @version: v1.0.0
	* @author: SF2170
	* @throws TemplateException 
	* @date: 2018年3月26日 上午9:28:59 
	*
	* Modification History:
	* Date         Author          Version            Description
	*---------------------------------------------------------*
	* 2018年3月26日     SF2170           v1.0.0               修改原因
	 */
	public void write(String templateName, Map<String, Object> map, String targetPath, String fileName) throws IOException, TemplateException {
		
		String thisPath = path + targetPath;
		File f1 = new File(thisPath);
		if (!f1.exists()) {
			f1.mkdirs();
		}
		File file = new File(thisPath + SPARATER + fileName);
		if (file.exists()) {
			if (kg == 2) { return ;}
			if (kg == 0) { 
				file.renameTo(new File(thisPath + SPARATER + fileName.replace(".java", "_Rename.java")));
				}
		}
		Configuration cfg = new Configuration(Configuration.VERSION_2_3_0);
		cfg.setDirectoryForTemplateLoading(new File(this.getClass().getResource("").getPath() + "ftl"));
		Template t = cfg.getTemplate(templateName);
		OutputStream out = new FileOutputStream(file);
		t.process(map, new OutputStreamWriter(out));
	}


	private void setClassTopMethod(BasicTableInfo basicTableInfo, List<String> list) {
		// 生成的java类位置
		list.add("package com.sefon.smarteconomy.domain;");// 配
		list.add("");
		list.add("");
		// 实体类中的标签导入位置
		list.add("import javax.persistence.Column;");
		list.add("import javax.persistence.Entity;");
		list.add("import javax.persistence.Table;");
		list.add(" ");
		list.add("/**");
		list.add(" * " );
		list.add(" * Copyright: Copyright (c) 2018 SefonSoft " );
		list.add(" * " );
		list.add(" * @ClassName: User.java " );
		list.add(" * @Description:"+ basicTableInfo.getTableDesc());
		list.add(" * " );
		list.add(" * @version: v1.0.0" );
		list.add(" * @author: SF2170" );
		list.add(" * @by AnalysisExcel2File.java" );
		list.add(" * @Date " + getNowTime());// 动态
		list.add(" * " );
		list.add(" */");
		list.add("@Entity");
		list.add("@Table(name=\""+basicTableInfo.getTableName()+"\")");
		list.add("public class " + basicTableInfo.getClassName() + " {");// TestBean
		list.add("");
	}


	private static StringBuffer setColumnProperty(List<String> list, Column col) throws RuntimeException{
		if (col.getIsId() != null) {
			list.add(6,"import javax.persistence.Id;");
			list.add("	@Id ");
		} 
		if (col.getName() == null) {
			throw new RuntimeException("字段名字不能为空 ！");
		}
		if (col.getType() == null) {
			throw new RuntimeException("字段类型不能为空 ！");
		}
		StringBuffer sb = new StringBuffer("	@Column(");//名字始终不为空 so 这里括号加起
		sb.append("name=\""+col.getName()+"\"");
	
		if (col.getIsNull() != null ) {
			sb.append(", nullable=false");
		}
		if (col.getUnique() != null ) {
			sb.append(", unique=true");
		}
		return sb;
	}


	private void setListEndMethod(List<String> listEnd, Column col) {
		String uStr = StringUtils.capitalize(col.getFieldName());
		listEnd.add("	public " + col.getType().getJavaName() + " get" + uStr + "() {");
		listEnd.add("		return " + col.getFieldName() + ";");
		listEnd.add("	}");
		listEnd.add("");

		listEnd.add("	public void set" + uStr + "(" + col.getType().getJavaName() + " " + col.getFieldName() + ") {");
		listEnd.add("		this." + col.getFieldName() + " = " + col.getFieldName() + ";");
		listEnd.add("	}");
		listEnd.add("");
	}

	/**
	 * 创建java类
	 * @param list	代码片段
	 * @param name	代码文件名
	 * @param ele 	表系统信息
	 */
	public void creatFile(List<String> list, String name) {
		String thisPath = path + TARGER_DIR_PATH + DOMAIN_PATH + SPARATER;
		File f1 = new File(thisPath);
		if (!f1.exists()) {
			f1.mkdirs();// 创建文件夹
		}
		String fileName = thisPath + name;
		File file = new File(fileName + ".java");
		if (file.exists()) {
			if (kg == 2) { return ;}
			if (kg == 0) { file.renameTo(new File(fileName + "_Rename.java"));}
		}
		try ( FileWriter fw = new FileWriter(file);
				BufferedWriter writer = new BufferedWriter(fw)) {
			for (String s : list) {
				writer.write(s);
				writer.newLine();// 换行
			}
			writer.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}


	// 算出重复的个数
	public int BeanNum(String name, String name2, int i) {
		try {
			Class.forName(name2);
			int beanNum = BeanNum(name, name + ++i, i);
			return beanNum;
		} catch (Exception e) {
			return i;
		}
	}


	/**
	 * 正则替换所有特殊字符 不包括"_"
	 * 
	 * @param orgStr
	 * @return
	 */
	public String replaceSpecStr(String orgStr) {
		if (null != orgStr && !"".equals(orgStr.trim())) {
			String regEx = "[\\s~·`!！@#￥$%^……&*（()）\\-——\\-=+【\\[\\]】｛{}｝\\|、\\\\；;：:‘'“”\"，,《<。.》>、/？?]";
			Pattern p = Pattern.compile(regEx);
			Matcher m = p.matcher(orgStr);
			return m.replaceAll("");
		}
		return null;
	}
	
	/**
	 * 去掉换行字符
	 * @param str
	 * @return
	 */
	private String removeChangeLineStr(String str) {
		return str.replace("\r", "").replace("\n", "").trim();
	}
	
	/**
	 * 替换下划线字符为驼峰式命名
	 * 
	 * @param str
	 * @return
	 */
	private String changeUnderlineStr(String str) {
		String[] strArray= str.split(UNDERLINE);
		if (strArray.length > 1) {
			StringBuffer sb = new StringBuffer(strArray[0]);
			for (int i = 1; i < strArray.length; i++) {
				sb.append(StringUtils.capitalize(strArray[i]));
			}
			return sb.toString();
		} else {
			return strArray[0];
		}
	}
	/**
	 * 判断以数字开头的字符串: true:是, false:否
	 * @param str
	 * @return
	 */
	public boolean isNumeric(String str) {
		Pattern pattern = Pattern.compile("[0-9]*");
		Matcher isNum = pattern.matcher(str.charAt(0) + "");
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}
	
	/**
	 * 获取当前时间
	 * @return
	 */
	public String getNowTime() {
		Date now = new Date(); 
		SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");//可以方便地修改日期格式
		String time = format.format(now);
		return time;
	}
	
	/**
	 * 获取当前时间
	 * @return
	 */
	public String getNowDate() {
		Date now = new Date(); 
		SimpleDateFormat format = new SimpleDateFormat("yyyy年MM月dd日");//可以方便地修改日期格式
		String time = format.format(now);
		return time;
	}
}
